#ifndef PW_PBL_COROUTINE_AWAITOR_HPP
#define PW_PBL_COROUTINE_AWAITOR_HPP

#include <coroutine>
#include <concepts>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
#include <cstdint>
#include "../IntFor.hpp"
#include "../Bitset.hpp"
#include "../RustMimic.hpp"

#if __has_include("config/pw_pbl/Coroutine.hpp")
#	include "config/pw_pbl/Coroutine.hpp"
#elif __has_include("config/pw_pbl/config.hpp")
#	include "config/pw_pbl/config.hpp"
#else
#	include "../default-config/Coroutine.hpp"
#endif

/// 协程名字空间
/// @note 本库的协程使用类似Javascript的单线程模型，多线程使用时各线程均可开启自己的协程执行器，
/// 但各线程的协程之间通信时需要另加同步机制，尤其注意co_await另一个线程的协程或等待器并不安全
namespace pw_pbl::co
{
	/// 协程自定义等待器模板类，本库协程中当co_await使用await_transform转换时首先尝试Await(obj)自动推导并转换
	/// @note 用户可添加推导指南和特化此模板类以实现特定的等待器
	/// 例如，通常使用特化模板来实现将std::chrono::duration转换为等待延时，以实现co_await 10ms;这样直观的表达式
	template <typename... T>
	struct Awaitor;
} // namespace pw_pbl::co

namespace pw_pbl::_co
{
	template <typename T>
	constexpr Option<T> convert_optional(Option<T> &&opt) { return std::move(opt); }
	template <typename T>
	constexpr Option<T> convert_optional(std::optional<T> &&opt)
	{
		if (opt.has_value())
			return std::move(*opt);
		else
			return None;
	}

	template <typename T>
	struct FullAwaitor;

	template <typename T>
	struct TinyAwaitor;

	template <typename C>
	concept ccSelfAwaitable = requires(C &&c) {
		{
			c.await_ready()
		} -> std::same_as<bool>;
		c.await_resume();
	};

	template <typename C>
	concept ccHasCoAwaitOperator = ccSelfAwaitable<decltype(std::declval<C>().operator co_await())>;

	template <typename F>
	concept ccAwaitable_func = requires(F &&f) {
		{
			f()
		} -> std::same_as<bool>;
	};

	template <typename F>
	concept ccAwaitableOption = requires(F &&f) { convert_optional(f()); };

	template <typename T>
	concept ccAwaitable = ccSelfAwaitable<T> || ccHasCoAwaitOperator<T> || ccAwaitable_func<T> || ccAwaitableOption<T>;

	template <typename T>
	struct _maybeOverride
	{
		using type = T;
	};

	template <typename T> requires requires { co::Awaitor(std::declval<T>()); }
	struct _maybeOverride<T>
	{
		using type = decltype(co::Awaitor(std::declval<T>()));
	};

	template <typename T>
	using MaybeOverride = typename _maybeOverride<T>::type;

	// 可被本库的协程直接转换为等待器，定义Awaitor<T>特化或符合标准库要求
	template <typename T>
	concept ccTransformable = ccAwaitable<MaybeOverride<T>>;

	template <typename T>
	using _tinyTransform = std::conditional_t<!ccHasCoAwaitOperator<T> && ccSelfAwaitable<T>, T, TinyAwaitor<T>>;

	template <ccTransformable T>
	using TinyTransform = _tinyTransform<MaybeOverride<T>>;

	template <bool... V>
	inline constexpr bool fNot = !(V || ...);

	template <typename T>
	FullAwaitor(T &&) -> FullAwaitor<MaybeOverride<T>>;

	template <ccAwaitable_func F> requires fNot<ccHasCoAwaitOperator<F>, ccSelfAwaitable<F>>
	struct TinyAwaitor<F>
	{
		F func;

		template <typename NF>
		inline TinyAwaitor(NF &&f) : func{std::forward<NF>(f)} {}

		inline bool await_ready() { return func(); }
		inline std::tuple<> await_resume() { return {}; }
	};

	template <ccAwaitableOption F> requires fNot<ccHasCoAwaitOperator<F>, ccSelfAwaitable<F>>
	struct TinyAwaitor<F>
	{
		F func;
		decltype(convert_optional(std::declval<F>()())) result;

		template <typename NF>
		inline TinyAwaitor(NF &&f) : func{std::forward<NF>(f)} {}

		inline bool await_ready()
		{
			result = convert_optional(func());
			return result.is_some();
		}
		inline auto await_resume() { return result.unwrap_unchecked(); }
	};

	template <ccSelfAwaitable T> requires fNot<ccHasCoAwaitOperator<T>>
	struct TinyAwaitor<T>
	{
		T obj;

		template <typename NT>
		inline TinyAwaitor(NT &&hdl) : obj{std::forward<NT>(hdl)} {}

		inline bool await_ready() { return obj.await_ready(); }
		inline decltype(auto) await_resume() { return obj.await_resume(); }
	};

	template <ccHasCoAwaitOperator BT>
	struct TinyAwaitor<BT>
	{
		using T = decltype(std::declval<BT>().operator co_await());

		T obj;

		template <typename NT>
		inline TinyAwaitor(NT &&hdl) : obj{std::forward<NT>(hdl).operator co_await()} {}

		inline bool await_ready() { return obj.await_ready(); }
		inline decltype(auto) await_resume() { return obj.await_resume(); }
	};

	template <ccAwaitable T>
	struct FullAwaitor<T> : public co::Executor::IAwaitable, public TinyAwaitor<T>
	{
		using IBase = co::Executor::IAwaitable;
		static_assert(std::convertible_to<IBase, co::IAwaitable<IBase>>);

		using TinyAwaitor<T>::await_ready;
		using TinyAwaitor<T>::await_resume;
		using co::IAwaitable<IBase>::await_suspend;

		static bool virtualReadyFunc(IBase &base) { return static_cast<FullAwaitor &>(base).await_ready(); }

		template <typename NT>
		inline FullAwaitor(NT &&hdl) : TinyAwaitor<T>{std::forward<NT>(hdl)} { co::IAwaitable<IBase>::ready_func = virtualReadyFunc; }
		FullAwaitor(const FullAwaitor &) = delete;
		FullAwaitor(FullAwaitor &&old) = default;

		FullAwaitor &operator=(const FullAwaitor &) = delete;
		FullAwaitor &operator=(FullAwaitor &&old) = delete;
	};

	template <>
	struct FullAwaitor<std::suspend_always> : public std::suspend_always
	{};
} // namespace pw_pbl::_co

namespace pw_pbl::co
{
	/// 下个循环等待器，挂起直到执行器的下个循环
	struct [[deprecated("Should use 'co_await co::Yield;' instead")]] NextTick
	{
		uint_fast8_t cnt = 0;
		bool await_ready() noexcept { return cnt++; }
		void await_resume() noexcept {}
	};

	/// 永久等待，协程版死循环的简化，co_await Forever()协程永远挂起不再被唤醒
	constexpr std::suspend_always Forever() { return {}; }

	/// 返回指定对象的立即完成等待器
	template <typename T>
	struct Ready
	{
		T obj;
		constexpr Ready(T &&v) noexcept : obj(std::forward<T>(v)) {}
		Ready(T &v) = delete;
		inline constexpr bool await_ready() noexcept { return true; }
		inline constexpr void await_suspend(std::coroutine_handle<>) noexcept {}
		template <typename SELF>
		inline constexpr decltype(auto) await_resume(this SELF &&self) noexcept { return std::forward_like<SELF>(self.obj); }
	};

	/// 比较等待器模板类，当指定的引用对象值等于指定值时等待完成
	/// @tparam T 所引用对象类型
	/// @tparam P 要比较的指定值
	/// @tparam OP 进行的比较算法，须支持OP(T,P)
	template <typename T, typename P, auto OP>
	struct Compare
	{
		T &cur;
		P tgt;
		template <typename NP>
		inline Compare(T &obj, NP &&t) : cur(obj), tgt(std::forward<NP>(t)) {}
		template <typename NP>
		inline Compare(std::reference_wrapper<T> obj, NP &&t) : cur(obj.get()), tgt(std::forward<NP>(t)) {}
		inline bool await_ready() { return OP(cur, tgt); }
		inline decltype(auto) await_resume() { return cur; }
	};

	template <typename T, typename P>
	inline constexpr Compare<T, P, std::equal_to<void>{}> Equal(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }
	template <typename T, typename P>
	inline constexpr Compare<T, P, std::not_equal_to<void>{}> NotEqual(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }
	template <typename T, typename P>
	inline constexpr Compare<T, P, std::greater<void>{}> Greater(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }
	template <typename T, typename P>
	inline constexpr Compare<T, P, std::less<void>{}> Less(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }
	template <typename T, typename P>
	inline constexpr Compare<T, P, std::greater_equal<void>{}> GreaterEqual(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }
	template <typename T, typename P>
	inline constexpr Compare<T, P, std::less_equal<void>{}> LessEuqal(T &cur, P &&t) noexcept { return {cur, std::forward<P>(t)}; }

	template <typename T>
	inline constexpr Compare<T, T, std::equal_to<void>{}> Equal(T &cur) noexcept { return {cur, T{cur}}; }
	template <typename T>
	inline constexpr Compare<T, T, std::not_equal_to<void>{}> NotEqual(T &cur) noexcept { return {cur, T{cur}}; }
	template <typename T>
	inline constexpr Compare<T, T, std::greater<void>{}> Greater(T &cur) noexcept { return {cur, T{cur}}; }
	template <typename T>
	inline constexpr Compare<T, T, std::less<void>{}> Less(T &cur) noexcept { return {cur, T{cur}}; }
	template <typename T>
	inline constexpr Compare<T, T, std::greater_equal<void>{}> GreaterEqual(T &cur) noexcept { return {cur, T{cur}}; }
	template <typename T>
	inline constexpr Compare<T, T, std::less_equal<void>{}> LessEuqal(T &cur) noexcept { return {cur, T{cur}}; }

	/// 并发与等待器模板类，当所有等待器完成时等待完成
	template <_co::ccSelfAwaitable... T>
	struct All
	{
		std::tuple<T...> awaitors;
		Bitset<sizeof...(T), UIntFor::Size<alignof(std::tuple<T...>)>> readys;

		template <typename... A>
		inline All(A &&...args) : awaitors{std::forward<A>(args)...}, readys{} {}
		All(const All &) = delete;
		All(All &&old) = default;

		All &operator=(const All &) = delete;
		All &operator=(All &&old) = default;

		template <size_t ID>
		constexpr int check_ready()
		{
			if (auto rd = this->readys[ID]; rd)
				return rd;
			auto ready = std::get<ID>(this->awaitors).await_ready();
			if (ready)
				this->readys.set(ID);
			return ready;
		}

		inline bool await_ready()
		{
			return [this]<size_t... IDS>(std::index_sequence<IDS...>) { return (this->check_ready<IDS>() & ...); }(std::make_index_sequence<sizeof...(T)>{});
		}
		inline decltype(auto) await_resume()
		{
			return std::apply([]<typename... P>(P &&...ators) { return std::tuple{std::forward<P>(ators).await_resume()...}; }, std::move(awaitors));
		}
	};
	template <typename... T>
	All(T &&...obj) -> All<_co::TinyTransform<T>...>;

	/// 并发或等待器模板类，当任意一个等待器完成时等待完成
	template <_co::ccSelfAwaitable... T>
	struct Any : protected All<T...>
	{
		template <typename... A>
		inline Any(A &&...args) : All<T...>{std::forward<A>(args)...} {}

		inline bool await_ready()
		{
			return [this]<size_t... IDS>(std::index_sequence<IDS...>) { return (this->template check_ready<IDS>() | ...); }(std::make_index_sequence<sizeof...(T)>{});
		}
		inline decltype(auto) await_resume()
		{
			return [this]<size_t... IDS>(std::index_sequence<IDS...>) { return std::tuple{this->get_result<IDS>()...}; }(std::make_index_sequence<sizeof...(T)>{});
		}

	private:
		template <size_t ID>
		constexpr decltype(auto) get_result()
		{
			if constexpr (std::is_void_v<decltype(std::get<ID>(std::move(this->awaitors)).await_resume())>)
				return this->readys[ID] ? (std::get<ID>(std::move(this->awaitors)).await_resume(), Some(std::tuple<>{})) : None;
			else
				return this->readys[ID] ? Some(std::get<ID>(std::move(this->awaitors)).await_resume()) : None;
		}
	};
	template <typename... T>
	Any(T &&...obj) -> Any<_co::TinyTransform<T>...>;

} // namespace pw_pbl::co

#endif // PW_PBL_COROUTINE_AWAITOR_HPP
